﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Cohen–Sutherland:
	http://en.wikipedia.org/wiki/Cohen%E2%80%93Sutherland
*******************************************************************************/

using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Diagnostics;
using System.IO;

namespace PKEngine
{
	/// <summary>
	/// The enum for pixel format.
	/// Only can use one pixel format in one game now.
	/// </summary>
	public enum PKPixelFormat
	{
		Argb1555,
		Rgb565,
		Rgb555,
		Argb4444
	}

	/// <summary>
	/// Surface of engine. Used for creating surface and draw it.
	/// </summary>
	public class PKSurface
	{
		#region Public Properties
		public string Name { get; private set; }
		public ushort[] Data { get; private set; }
		public PKPixelFormat PixelFormat { get; private set; }
		public Size ContentSize { get; private set; }

		/// <summary>
		/// Get the ViewRect for surface.
		/// </summary>
		public virtual Rectangle ClippingRect
		{
			get
			{
				return new Rectangle(0, 0, this.ContentSize.Width, this.ContentSize.Height);
			}
		}
		#endregion

		#region Initialization
		public PKSurface(Size contentSize)
			: this(contentSize, PKConfig.SharedConfig.PixelFormat)
		{
		}

		internal PKSurface(Size contentSize, PKPixelFormat pixelFormat)
		{
			Debug.Assert(contentSize.Width > 0 && contentSize.Height > 0, "PKSurface: Invalid size.");

			this.ContentSize = contentSize;
			this.PixelFormat = pixelFormat;
			this.Name = string.Empty;
			this.Data = new ushort[contentSize.Width * contentSize.Height];
		}

		public PKSurface(string filePath)
			: this(filePath, PKConfig.SharedConfig.PixelFormat)
		{
		}

		internal PKSurface(string filePath, PKPixelFormat pixelFormat)
		{
			Debug.Assert(!string.IsNullOrEmpty(filePath), "PKSurface: Argument cannot be null.");
			Debug.Assert(File.Exists(filePath), "PKSurface: Cannot find file.");

			using (Bitmap bitmap = new Bitmap(filePath))
			{
				this.Name = filePath.ToLower();
				this.PixelFormat = pixelFormat;
				this.ContentSize = bitmap.Size;

				int length = bitmap.Size.Width * bitmap.Size.Height;
				this.Data = new ushort[length];

				System.Drawing.Imaging.PixelFormat pf;
				switch (pixelFormat)
				{
					case PKPixelFormat.Argb1555:
						// ARRRRRGGGGGBBBBB
						pf = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
						break;
					case PKPixelFormat.Rgb565:
						// RRRRRGGGGGGBBBBB
						pf = System.Drawing.Imaging.PixelFormat.Format16bppRgb565;
						break;
					case PKPixelFormat.Rgb555:
						// 0RRRRRGGGGGBBBBB
						pf = System.Drawing.Imaging.PixelFormat.Format16bppRgb555;
						break;
					case PKPixelFormat.Argb4444:
						// AAAARRRRGGGGBBBB
						pf = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
						break;
					default:
						// Invalid value.
						pf = System.Drawing.Imaging.PixelFormat.Undefined;
						break;
				}

				byte[] temp;
				BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, pf);
				switch (pixelFormat)
				{
					case PKPixelFormat.Rgb565:
					case PKPixelFormat.Rgb555:
						// Copy data directly.
						temp = new byte[length * 2];
						System.Runtime.InteropServices.Marshal.Copy(data.Scan0, temp, 0, length * 2);
						for (int i = 0; i < length; i++)
						{
							this.Data[i] = BitConverter.ToUInt16(temp, i * 2);
						}
						break;
					case PKPixelFormat.Argb1555:
						// Convert from 32bpp to 16bpp.
						temp = new byte[length * 4];
						System.Runtime.InteropServices.Marshal.Copy(data.Scan0, temp, 0, length * 4);
						for (int i = 0; i < length; i++)
						{
							if (temp[i * 4 + 3] == 0)
							{
								// Transparent color.
								this.Data[i] = 0x7C1F;
							}
							else
							{
								// From AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB to ARRRRRGGGGGBBBBB.
								uint argb8888 = BitConverter.ToUInt32(temp, i * 4);
								this.Data[i] = (ushort)(((argb8888 >> 16) & 0x8000) | // A
														((argb8888 >> 9) & 0x7C00) | // R
														((argb8888 >> 6) & 0x3E0) | // G
														((argb8888 >> 3) & 0x1F)); // B
							}
						}
						break;
					case PKPixelFormat.Argb4444:
						// Convert from 32bpp to 16bpp.
						temp = new byte[length * 4];
						System.Runtime.InteropServices.Marshal.Copy(data.Scan0, temp, 0, length * 4);
						for (int i = 0; i < length; i++)
						{
							if (temp[i * 4 + 3] == 0)
							{
								// Transparent color.
								this.Data[i] = 0x0F0F;
							}
							else
							{
								// From AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB to AAAARRRRGGGGBBBB.
								uint argb8888 = BitConverter.ToUInt32(temp, i * 4);
								this.Data[i] = (ushort)(((argb8888 >> 16) & 0xF000) | // A
														((argb8888 >> 12) & 0xF00) | // R
														((argb8888 >> 8) & 0xF0) | // G
														((argb8888 >> 4) & 0xF)); // B
							}
						}
						break;
				}
				bitmap.UnlockBits(data);
			}
		}
		#endregion

		#region Pixel Methods
		/// <summary>
		/// Gets color of a pixel.
		/// </summary>
		/// <param name="x">X</param>
		/// <param name="y">Y</param>
		/// <returns>Color</returns>
		public ushort GetPixel(int x, int y)
		{
			Debug.Assert(x >= 0 && x < ContentSize.Width && y >= 0 && y < ContentSize.Height, "PKSurface.GetPixel Invalid point.");

			return Data[y * ContentSize.Width + x];
		}

		/// <summary>
		/// Sets color for a pixel.
		/// </summary>
		/// <param name="x">X</param>
		/// <param name="y">Y</param>
		/// <param name="value">Color</param>
		public void SetPixel(int x, int y, ushort value)
		{
			if (x < 0 || y < 0 || x >= ContentSize.Width || y >= ContentSize.Height)
			{
				return;
			}

			Data[y * ContentSize.Width + x] = value;
		}
		#endregion

		#region Draw Methods
		/// <summary>
		/// Draw line on surface using specified color.
		/// </summary>
		/// <param name="dPoint1">Point 1</param>
		/// <param name="dPoint2">Point 2</param>
		/// <param name="color">Color</param>
		public void DrawLine(Point dPoint1, Point dPoint2, ushort color)
		{
			if (ClipTwoPoints(ref dPoint1, ref dPoint2))
			{
				PKAlpha.SharedAlpha.DrawLine(this, dPoint1, dPoint2, color);
			}
		}

		/// <summary>
		/// Draw rectangle on surface using specified color.
		/// </summary>
		/// <param name="dRect">Destination rectangle</param>
		/// <param name="color">Color</param>
		public void DrawRectangle(Rectangle dRect, ushort color)
		{
			if (ClipRect(ref dRect))
			{
				PKAlpha.SharedAlpha.DrawRectangle(this, dRect, color);
			}
		}
		#endregion

		#region BitBlt Methods
		/// <summary>
		/// Clears the surface using default color key.
		/// </summary>
		/// <param name="useColorKey">If we should use color key</param>
		public void Clear(bool useColorKey)
		{
			if (useColorKey)
			{
				this.Clear(PKAlpha.SharedAlpha.ColorKey);
			}
			else
			{
				this.Clear(PKAlpha.SharedAlpha.ColorBlack);
			}
		}

		/// <summary>
		/// Clears the surface using specified color.
		/// </summary>
		/// <param name="color">Color</param>
		public void Clear(ushort color)
		{
			this.Fill(new Rectangle(0, 0, ContentSize.Width, ContentSize.Height), color);
		}

		/// <summary>
		/// Fills the surface using default colorkey.
		/// </summary>
		/// <param name="dRect">Destination rectangle</param>
		/// <param name="useColorKey">If we should use color key</param>
		public void Fill(Rectangle dRect, bool useColorKey)
		{
			if (useColorKey)
			{
				this.Fill(dRect, PKAlpha.SharedAlpha.ColorKey);
			}
			else
			{
				this.Fill(dRect, PKAlpha.SharedAlpha.ColorBlack);
			}
		}

		/// <summary>
		/// Fills the surface using specified color.
		/// </summary>
		/// <param name="dRect">Destination rectangle</param>
		/// <param name="color">Color</param>
		public void Fill(Rectangle dRect, ushort color)
		{
			if (ClipRect(ref dRect))
			{
				PKAlpha.SharedAlpha.Fill(this, dRect, color);
			}
		}

		/// <summary>
		/// Fills the surface using specified color with alpha blend.
		/// </summary>
		/// <param name="dRect">Destination rectangle</param>
		/// <param name="color">Color</param>
		/// <param name="alpha">alpha value (0 ~ 256)</param>
		public void FillAlpha(Rectangle dRect, ushort color, int alpha)
		{
			if (alpha <= 0 || color == PKAlpha.SharedAlpha.ColorKey)
			{
				return;
			}

			if (ClipRect(ref dRect))
			{
				PKAlpha.SharedAlpha.FillAlpha(this, dRect, color, alpha);
			}
		}

		/// <summary>
		/// Fills the surface using specified color with alpha blend.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sChannel">source channel</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="color">Color</param>
		public void FillWithAlphaChannel(int dX, int dY, PKChannel sChannel, Rectangle sRect, ushort color)
		{
			if (color == PKAlpha.SharedAlpha.ColorKey)
			{
				return;
			}

			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.FillWithAlphaChannel(this, dX, dY, sChannel, sRect, color);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		public void Blt(int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.Blt(this, dX, dY, sSurface, sRect);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with stretch.
		/// </summary>
		/// <param name="dRect">destination rectangle</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		public void BltStretch(Rectangle dRect, PKSurface sSurface, Rectangle sRect)
		{
			if (ClipRect(ref dRect, ref sRect))
			{
				PKAlpha.SharedAlpha.BltStretch(this, dRect, sSurface, sRect);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with alpha blend.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="alpha">alpha value (0 ~ 256)</param>
		/// <remarks>Source surface will be fully transparent if alpha value is zero.</remarks>
		public void BltAlpha(int dX, int dY, PKSurface sSurface, Rectangle sRect, int alpha)
		{
			if (alpha <= 0)
			{
				return;
			}

			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltAlpha(this, dX, dY, sSurface, sRect, alpha);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with alpha blend & stretch.
		/// </summary>
		/// <param name="dRect">destination rectangle</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="alpha">alpha value (0 ~ 256)</param>
		/// <remarks>Source surface will be fully transparent if alpha value is zero.</remarks>
		public void BltAlphaStretch(Rectangle dRect, PKSurface sSurface, Rectangle sRect, int alpha)
		{
			if (alpha <= 0)
			{
				return;
			}

			if (ClipRect(ref dRect, ref sRect))
			{
				PKAlpha.SharedAlpha.BltAlphaStretch(this, dRect, sSurface, sRect, alpha);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with additive mixing.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		public void BltAdd(int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltAdd(this, dX, dY, sSurface, sRect);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with subtractive mixing.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		public void BltSub(int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltSub(this, dX, dY, sSurface, sRect);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with gray color.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		public void BltGray(int dX, int dY, PKSurface sSurface, Rectangle sRect)
		{
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltGray(this, dX, dY, sSurface, sRect);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with hue.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="hue">target hue</param>
		public void BltHue(int dX, int dY, PKSurface sSurface, Rectangle sRect, int hue)
		{
			if (hue < 0 || hue > 359)
			{
				return;
			}

			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltHue(this, dX, dY, sSurface, sRect, hue);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with tint.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="color">target color</param>
		/// <param name="colorAlpha">alpha value of color(0 ~ 256)</param>
		public void BltTint(int dX, int dY, PKSurface sSurface, Rectangle sRect, ushort color, int colorAlpha)
		{
			if (color == PKAlpha.SharedAlpha.ColorKey)
			{
				return;
			}

			if (colorAlpha <= 0)
			{
				this.BltAlpha(dX, dY, sSurface, sRect, 256);
			}

			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltTint(this, dX, dY, sSurface, sRect, color, colorAlpha);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with color.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="color">target color</param>
		public void BltColor(int dX, int dY, PKSurface sSurface, Rectangle sRect, ushort color)
		{
			if (color == PKAlpha.SharedAlpha.ColorKey)
			{
				return;
			}

			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltColor(this, dX, dY, sSurface, sRect, color);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with transition channel.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="channel">transition channel</param>
		/// <param name="value">transition value</param>
		public void BltWithTransitionChannel(int dX, int dY, PKSurface sSurface, Rectangle sRect, PKChannel channel, int value)
		{
			if (channel == null || value <= 0)
			{
				return;
			}

			Debug.Assert(sSurface.ContentSize.Equals(channel.ContentSize), "PKSurface.BltWithTransitionChannel: The size of channel does not match the size of surface.");
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltWithTransitionChannel(this, dX, dY, sSurface, sRect, channel, value);
			}
		}

		/// <summary>
		/// Blt from a surface to this surface with alpha channel.
		/// </summary>
		/// <param name="dX">destination X</param>
		/// <param name="dY">destination Y</param>
		/// <param name="sSurface">source surface</param>
		/// <param name="sRect">source rectangle</param>
		/// <param name="channel">alpha channel</param>
		public void BltWithAlphaChannel(int dX, int dY, PKSurface sSurface, Rectangle sRect, PKChannel channel)
		{
			if (channel == null)
			{
				return;
			}

			Debug.Assert(sSurface.ContentSize.Equals(channel.ContentSize), "PKSurface.BltWithAlphaChannel: The size of channel does not match the size of surface.");
			if (ClipRect(ref dX, ref dY, ref sRect))
			{
				PKAlpha.SharedAlpha.BltWithAlphaChannel(this, dX, dY, sSurface, sRect, channel);
			}
		}
		#endregion

		#region Protected Methods
		private int GetPointCode(Point point)
		{
			int code = 0;

			Rectangle clippingRect = this.ClippingRect;
			if (point.X < clippingRect.Left)
			{
				code |= 0x1;
			}
			else if (point.X > clippingRect.Right)
			{
				code |= 0x2;
			}
			if (point.Y < clippingRect.Top)
			{
				code |= 0x4;
			}
			else if (point.Y > clippingRect.Bottom)
			{
				code |= 0x8;
			}

			return code;
		}

		protected bool ClipTwoPoints(ref Point point1, ref Point point2)
		{
			// Cohen–Sutherland algorithm.
			int code1 = this.GetPointCode(point1);
			int code2 = this.GetPointCode(point2);
			bool accept = false;
			Rectangle clippingRect = this.ClippingRect;
			while (true)
			{
				if ((code1 | code2) == 0)
				{
					accept = true;
					break;
				}
				else if ((code1 & code2) != 0)
				{
					break;
				}
				else
				{
					int x, y;
					int codeOut = code1 != 0 ? code1 : code2;
					if ((codeOut & 0x8) > 0)
					{
						x = point1.X + (point2.X - point1.X) * (clippingRect.Bottom - point1.Y) / (point2.Y - point1.Y);
						y = clippingRect.Bottom;
					}
					else if ((codeOut & 0x4) > 0)
					{
						x = point1.X + (point2.X - point1.X) * (clippingRect.Top - point1.Y) / (point2.Y - point1.Y);
						y = clippingRect.Top;
					}
					else if ((codeOut & 0x2) > 0)
					{
						y = point1.Y + (point2.Y - point1.Y) * (clippingRect.Right - point1.X) / (point2.X - point1.X);
						x = clippingRect.Right;
					}
					else
					{
						y = point1.Y + (point2.Y - point1.Y) * (clippingRect.Left - point1.X) / (point2.X - point1.X);
						x = clippingRect.Left;
					}
					if (codeOut == code1)
					{
						point1.X = x;
						point1.Y = y;
						code1 = GetPointCode(point1);
					}
					else
					{
						point2.X = x;
						point2.Y = y;
						code2 = GetPointCode(point2);
					}
				}
			}
			return accept;
		}

		protected bool ClipRect(ref Rectangle dRect)
		{
			Rectangle clippingRect = this.ClippingRect;
			if (dRect.X < clippingRect.X)
			{
				dRect.Width -= clippingRect.X - dRect.X;
				dRect.X = clippingRect.X;
			}
			if (dRect.Right > clippingRect.Right)
			{
				dRect.Width = clippingRect.Right - dRect.X;
			}
			if (dRect.Y < clippingRect.Y)
			{
				dRect.Height -= clippingRect.Y - dRect.Y;
				dRect.Y = clippingRect.Y;
			}
			if (dRect.Bottom > clippingRect.Bottom)
			{
				dRect.Height = clippingRect.Bottom - dRect.Y;
			}
			return (dRect.Width > 0 && dRect.Height > 0);
		}

		protected bool ClipRect(ref int dX, ref int dY, ref Rectangle sRect)
		{
			Rectangle clippingRect = this.ClippingRect;
			if (dX < clippingRect.X)
			{
				sRect.X += clippingRect.X - dX;
				sRect.Width -= clippingRect.X - dX;
				dX = clippingRect.X;
			}
			if (dX + sRect.Width > clippingRect.Right)
			{
				sRect.Width = clippingRect.Right - dX;
			}
			if (dY < clippingRect.Y)
			{
				sRect.Y += clippingRect.Y - dY;
				sRect.Height -= clippingRect.Y - dY;
				dY = clippingRect.Y;
			}
			if (dY + sRect.Height > clippingRect.Bottom)
			{
				sRect.Height = clippingRect.Bottom - dY;
			}
			return (sRect.Width > 0 && sRect.Height > 0);
		}

		protected bool ClipRect(ref Rectangle dRect, ref Rectangle sRect)
		{
			float scaleX = (float)dRect.Width / sRect.Width;
			float scaleY = (float)dRect.Height / sRect.Height;
			if (scaleX <= 0 || scaleY <= 0)
			{
				return false;
			}

			Rectangle clippingRect = this.ClippingRect;
			int diff;
			if (dRect.X < clippingRect.X)
			{
				diff = clippingRect.X - dRect.X;
				sRect.Width -= (int)(diff / scaleX);
				sRect.X += (int)(diff / scaleX);
				dRect.Width -= diff;
				dRect.X = clippingRect.X;
			}
			if (dRect.Right > clippingRect.Right)
			{
				diff = dRect.Right - clippingRect.Right;
				sRect.Width -= (int)(diff / scaleX);
				dRect.Width -= diff;
			}
			if (dRect.Y < 0)
			{
				diff = clippingRect.Y - dRect.Y;
				sRect.Width -= (int)(diff / scaleY);
				sRect.Y += (int)(diff / scaleY);
				dRect.Width -= diff;
				dRect.Y = clippingRect.Y;
			}
			if (dRect.Bottom > ContentSize.Height)
			{
				diff = dRect.Bottom - clippingRect.Bottom;
				sRect.Height -= (int)(diff / scaleY);
				dRect.Height -= diff;
			}
			return (sRect.Width > 0 && sRect.Height > 0);
		}
		#endregion
	}
}